/*
 *   Copyright 2012-present OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

// Packages
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { cloneDeep, isEmpty } from 'lodash';
import {
  Button,
  Callout,
  Checkbox,
  Classes,
  ControlGroup,
  Dialog,
  FormGroup,
  InputGroup,
  Intent,
  Position
} from '@blueprintjs/core';

// Services
import { RepositoryService } from '../../../services';

// UI
import { Loading } from '../../UI';

// Utils
import { Saiku } from '../../../utils';
import { PRIVATE, SECURED, READ, WRITE, GRANT } from '../../../utils/constants';

// Styles
import './PermissionsDialog.css';

class PermissionsDialog extends Component {
  _isMounted = false;

  state = {
    owner: '',
    role: '',
    rolesAcl: {},
    isAclRead: false,
    isAclWrite: false,
    isAclGrant: false,
    isNotPrivateOwner: false,
    isAclPrivate: false,
    loadingRules: true,
    loading: false,
    error: false
  };

  componentDidMount() {
    this._isMounted = true;
    this.callApiGetAclObjects();
  }

  componentWillUnmount() {
    this._isMounted = false;
    RepositoryService.cancelRequest();
  }

  callApiGetAclObjects = () => {
    const { itemPath, user } = this.props;
    const { username } = user;
    const newItemPath = this.checkFolderPath(itemPath)
      ? Saiku.trimFirstLastChar(itemPath, 'last')
      : itemPath;

    this.setState({ loadingRules: true });

    RepositoryService.getAclObjects(newItemPath)
      .then(res => {
        if (this._isMounted && res.status === 200) {
          const { data } = res;
          const { owner, roles, type } = data;
          const isOwner = username === owner;
          const isAclPrivate = type === PRIVATE;

          this.setState({
            owner,
            rolesAcl: roles || {},
            isNotPrivateOwner: !isOwner && isAclPrivate,
            isAclPrivate: isOwner && isAclPrivate,
            loadingRules: false
          });
        } else {
          if (this._isMounted) {
            this.setState({ loadingRules: false });
          }
        }
      })
      .catch(error => {
        if (this._isMounted) {
          this.setState({ loadingRules: false });
        }
      });
  };

  checkFolderPath(value) {
    return value.indexOf('/', value.length - 1) !== -1;
  }

  addRole = () => {
    const { role, rolesAcl, isAclRead, isAclWrite, isAclGrant } = this.state;
    const acls = [];

    if (!isEmpty(role)) {
      if (isAclRead) {
        acls.push(READ);
      }

      if (isAclWrite) {
        acls.push(WRITE);
      }

      if (isAclGrant) {
        acls.push(GRANT);
      }

      if (!isEmpty(acls)) {
        this.setState({
          role: '',
          rolesAcl: Object.assign({}, rolesAcl, { [role]: acls }),
          isAclRead: false,
          isAclWrite: false,
          isAclGrant: false,
          error: false
        });
      } else {
        this.setState({ error: true });
      }
    }

    return false;
  };

  handleRoleChange = event => {
    const { value } = event.target;
    const isEnterKey = event.keyCode === 13;

    this.setState({ role: value }, () => {
      if (isEnterKey) {
        this.addRole();
      }
    });
  };

  handleAclChange(name) {
    this.setState(prevState => ({
      [name]: !prevState[name]
    }));
  }

  handleKeepPrivateChange = () => {
    this.setState(prevState => ({
      isAclPrivate: !prevState.isAclPrivate
    }));
  };

  handleRemoveAcl = () => {
    const { rolesAcl } = this.state;
    const rolesAclClone = cloneDeep(rolesAcl);
    const options = this.roleListNode && this.roleListNode.options;
    const len = options.length;
    const acls = [];
    let opt;

    for (let i = 0; i < len; i++) {
      opt = options[i];

      if (opt.selected) {
        acls.push(opt.value);
      }
    }

    acls.forEach(acl => delete rolesAclClone[acl]);

    this.setState({ rolesAcl: rolesAclClone });
  };

  handleSave = () => {
    const { itemPath, user, onClose } = this.props;
    const { rolesAcl, isAclPrivate } = this.state;
    const { username } = user;
    const newItemPath = this.checkFolderPath(itemPath)
      ? Saiku.trimFirstLastChar(itemPath, 'last')
      : itemPath;
    let acl = {};

    this.setState({ loading: true });

    if (isAclPrivate) {
      acl = { type: PRIVATE, owner: username };
    } else {
      acl = { type: SECURED, roles: rolesAcl, owner: username };
    }

    RepositoryService.postAclObject(newItemPath, JSON.stringify(acl))
      .then(res => {
        this.setState({ loading: false });

        if (res.status === 200) {
          Saiku.toasts(Position.TOP_RIGHT).show({
            icon: 'tick',
            intent: Intent.SUCCESS,
            message: 'Permissions added'
          });

          onClose();
        } else {
          Saiku.toasts(Position.TOP_RIGHT).show({
            icon: 'error',
            intent: Intent.DANGER,
            message: 'An error occurred while setting permissions'
          });
        }
      })
      .catch(error => {
        Saiku.toasts(Position.TOP_RIGHT).show({
          icon: 'error',
          intent: Intent.DANGER,
          message: 'Something went wrong'
        });
      });
  };

  renderError() {
    return (
      <Callout
        className="m-b-20"
        intent={Intent.DANGER}
        style={{ color: '#ee5342' }}
      >
        You need to chose at least one ACL method for this role.
      </Callout>
    );
  }

  renderForm() {
    const { user, onClose } = this.props;
    const { username } = user;
    const {
      owner,
      role,
      rolesAcl,
      isAclRead,
      isAclWrite,
      isAclGrant,
      isNotPrivateOwner,
      isAclPrivate,
      loadingRules,
      loading,
      error
    } = this.state;

    return (
      <Fragment>
        <div className={Classes.DIALOG_BODY}>
          {!loadingRules ? (
            <Fragment>
              {!isAclPrivate && (
                <Fragment>
                  {error && this.renderError()}
                  <FormGroup label="Add role permission" labelFor="role">
                    <ControlGroup fill>
                      <InputGroup
                        value={role}
                        onChange={this.handleRoleChange}
                        onKeyDown={this.handleRoleChange}
                        autoFocus
                      />
                      <Button
                        text="Add"
                        intent={Intent.DANGER}
                        onClick={this.addRole}
                      />
                    </ControlGroup>
                    <Checkbox
                      label={READ}
                      checked={isAclRead}
                      onChange={this.handleAclChange.bind(this, 'isAclRead')}
                      inline
                    />
                    <Checkbox
                      label={WRITE}
                      checked={isAclWrite}
                      onChange={this.handleAclChange.bind(this, 'isAclWrite')}
                      inline
                    />
                    <Checkbox
                      label={GRANT}
                      checked={isAclGrant}
                      onChange={this.handleAclChange.bind(this, 'isAclGrant')}
                      inline
                    />
                  </FormGroup>
                  <FormGroup label="Roles" labelFor="roleList">
                    <select
                      ref={node => (this.roleListNode = node)}
                      className="sku-select-roles"
                      size="4"
                      multiple
                    >
                      {Object.keys(rolesAcl).map(role => (
                        <option key={Saiku.uid(role)} value={role}>
                          {role} [{rolesAcl[role].join(', ')}]
                        </option>
                      ))}
                    </select>
                  </FormGroup>
                  <FormGroup>
                    <Button
                      text="Remove permission"
                      intent={Intent.DANGER}
                      disabled={isEmpty(rolesAcl)}
                      onClick={this.handleRemoveAcl}
                    />
                  </FormGroup>
                </Fragment>
              )}
              <FormGroup>
                <Checkbox
                  label={
                    <span>
                      Keep it private for me (<b>{username}</b>)
                    </span>
                  }
                  checked={isAclPrivate}
                  onChange={this.handleKeepPrivateChange}
                  inline
                />
              </FormGroup>

              {isNotPrivateOwner && (
                <FormGroup>
                  <p>
                    Currently private to: <b>{owner}</b>
                  </p>
                </FormGroup>
              )}
            </Fragment>
          ) : (
            <Loading className="m-t-20" size={30} center />
          )}
        </div>
        <div className={Classes.DIALOG_FOOTER}>
          <div className={Classes.DIALOG_FOOTER_ACTIONS}>
            <Button
              text="Save"
              intent={Intent.DANGER}
              loading={loading}
              onClick={this.handleSave}
            />
            <Button text="Close" onClick={onClose} />
          </div>
        </div>
      </Fragment>
    );
  }

  render() {
    const { onClose } = this.props;

    return (
      <Dialog
        className="sku-permissions-dialog"
        title="Permissions"
        icon="people"
        canOutsideClickClose={false}
        isOpen={true}
        onClose={onClose}
      >
        {this.renderForm()}
      </Dialog>
    );
  }
}

PermissionsDialog.propTypes = {
  itemPath: PropTypes.string.isRequired,
  user: PropTypes.object.isRequired,
  onClose: PropTypes.func.isRequired
};

const mapStateToProps = state => ({
  ...state.session
});

export default connect(mapStateToProps)(PermissionsDialog);
